﻿using System;
using System.Runtime.InteropServices;

namespace MceFM.Last.KeyHook {
  internal class HookStuff {
    //This code is derived from http://msdn.microsoft.com/msdnmag/issues/02/10/CuttingEdge/

    public class HookEventArgs : EventArgs {
      public int HookCode; // Hook code
      public int wParam; // WPARAM argument
      public IntPtr lParam; // LPARAM argument
    }

#pragma warning disable 169
#pragma warning disable 649
    [Serializable, StructLayout(LayoutKind.Sequential)]
    private class KeyboardHookStruct
    {
      internal int vkCode;	//Specifies a virtual-key code. The code must be a value in the range 1 to 254. 
      internal int scanCode; // Specifies a hardware scan code for the key. 
      internal int flags;  // Specifies the extended-key flag, event-injected flag, context code, and transition-state flag.
      internal int time; // Specifies the time stamp for this message.
      internal IntPtr dwExtraInfo; // Specifies extra information associated with the message. 
#pragma warning restore 169
#pragma warning restore 649
    }

    public static int GetKeyPressed(HookEventArgs hookEventArgs) {
      KeyboardHookStruct keyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(hookEventArgs.lParam, typeof(KeyboardHookStruct));
      return keyboardHookStruct.vkCode;
    }


    // Hook Types  
    public enum HookType {
      WH_JOURNALRECORD = 0,
      WH_JOURNALPLAYBACK = 1,
      WH_KEYBOARD = 2,
      WH_GETMESSAGE = 3,
      WH_CALLWNDPROC = 4,
      WH_CBT = 5,
      WH_SYSMSGFILTER = 6,
      WH_MOUSE = 7,
      WH_HARDWARE = 8,
      WH_DEBUG = 9,
      WH_SHELL = 10,
      WH_FOREGROUNDIDLE = 11,
      WH_CALLWNDPROCRET = 12,
      WH_KEYBOARD_LL = 13,
      WH_MOUSE_LL = 14
    }

    public class LocalWindowsHook {
      // ****************************************************************

      // Filter function delegate
      public delegate int HookProc(int code, int wParam, IntPtr lParam);

      // ****************************************************************

      // ****************************************************************

      // Internal properties
      protected IntPtr m_hhook = IntPtr.Zero;
      protected HookProc m_filterFunc = null;
      protected HookType m_hookType;

      // ****************************************************************

      // ****************************************************************

      // Event delegate
      public delegate int HookEventHandler(object sender, HookEventArgs e);

      // ****************************************************************

      // ****************************************************************

      // Event: HookInvoked 
      public event HookEventHandler HookInvoked;

      protected int OnHookInvoked(HookEventArgs e) {
        if(HookInvoked != null) {
          return HookInvoked(this, e);
        }
        return 0;
      }

      // ****************************************************************

      // ****************************************************************

      // Class constructor(s)
      public LocalWindowsHook(HookType hook) {
        m_hookType = hook;
        m_filterFunc = CoreHookProc;
      }

      public LocalWindowsHook(HookType hook, HookProc func) {
        m_hookType = hook;
        m_filterFunc = func;
      }

      // ****************************************************************

      // ****************************************************************

      // Default filter function
      public int CoreHookProc(int code, int wParam, IntPtr lParam) {
        if(code < 0) {
          return CallNextHookEx(m_hhook, code, wParam, lParam);
        }

        // Let clients determine what to do
        HookEventArgs e = new HookEventArgs();
        e.HookCode = code;
        e.wParam = wParam;
        e.lParam = lParam;
        int retVal = OnHookInvoked(e);
        return retVal == 0 ? CallNextHookEx(m_hhook, code, wParam, lParam) : retVal;
      }

      // ****************************************************************

      // ****************************************************************

      // Install the hook
      public bool Install() {
#pragma warning disable 612,618
        return Install(IntPtr.Zero, AppDomain.GetCurrentThreadId());
#pragma warning restore 612,618
      }

      public bool Install(IntPtr hMod, int threadID) {
        m_hhook = SetWindowsHookEx(
          m_hookType,
          m_filterFunc,
          hMod,
          threadID);
        return m_hhook != IntPtr.Zero;
      }

      // ****************************************************************

      // ****************************************************************

      // Uninstall the hook
      public void Uninstall() {
        UnhookWindowsHookEx(m_hhook);
      }

      // Win32: SetWindowsHookEx()
      [DllImport("user32.dll")]
      protected static extern IntPtr SetWindowsHookEx(HookType code,
                                                      HookProc func,
                                                      IntPtr hInstance,
                                                      int threadID);

      // Win32: UnhookWindowsHookEx()
      [DllImport("user32.dll")]
      protected static extern int UnhookWindowsHookEx(IntPtr hhook);

      // Win32: CallNextHookEx()
      [DllImport("user32.dll")]
      protected static extern int CallNextHookEx(IntPtr hhook,
                                                 int code,
                                                 int wParam,
                                                 IntPtr lParam);
    }
  }
}